#ifndef __M_MATCHER_H__
#define __M_MATCHER_H__

#include "util.hpp"
#include "online.hpp"
#include "db.hpp"
#include "room.hpp"
#include <list>
#include <condition_variable>
template <class T>

//双向链表作为一个队列来管理匹配玩家
class match_queue {
    private:
        /*用链表而不直接使用queue是因为我们有中间删除数据的需要*/
        std::list<T> _list;//列表中存放不同的元素，更改模版的类型
        /*实现线程安全*/
        std::mutex _mutex;
        /*这个条件变量主要为了阻塞消费者，后边使用的时候：队列中元素个数<2则阻塞*/
        std::condition_variable _cond;//多线程编程中实现线程同步的重要工具
    public:
        /*获取元素个数*/
        int size() {  
            std::unique_lock<std::mutex> lock(_mutex);
            return _list.size(); 
        }
        /*判断是否为空*/
        bool empty() {
            std::unique_lock<std::mutex> lock(_mutex);
            return _list.empty();
        }
        /*阻塞线程*/
        void wait() {
            std::unique_lock<std::mutex> lock(_mutex);//获取锁
            _cond.wait(lock);//wait() 调用会自动释放 _mutex，允许其他线程获取该锁，线程状态变为阻塞(blocked)
        }
        /*入队数据，并唤醒线程*/
        void push(const T &data) {
            std::unique_lock<std::mutex> lock(_mutex);
            _list.push_back(data);
            _cond.notify_all();//唤醒一个线程，操作系统将线程状态从阻塞变为就绪，线程被调度器选中后，重新自动获取 _mutex 锁
        }
        /*出队数据*/
        bool pop(T &data) {
            std::unique_lock<std::mutex> lock(_mutex);
            if (_list.empty() == true) {
                return false;
            }
            data = _list.front();
            _list.pop_front();
            return true;
        }
        /*移除指定的数据*/
        void remove(T &data) {
            std::unique_lock<std::mutex> lock(_mutex);
            _list.remove(data);
        }
};

//匹配处理
class matcher{
    private:
        //根据不同的段位分成三个匹配队列
        match_queue<uint64_t> _q_normal;
        match_queue<uint64_t> _q_high;
        match_queue<uint64_t> _q_super;
        //为三个匹配队列定义三个处理线程
        std::thread _th_normal;
        std::thread _th_high;
        std::thread _th_super;
        
        room_manager *_rm;
        user_table *_ut;
        online_manager *_om;

    private:
        //匹配处理函数
        void handle_match(match_queue<uint64_t> &mq){
            while(1){
                //判断队列人数是否大于2，<2则阻塞等待
                while(mq.size() < 2){
                    mq.wait();
                }
                //走到这代表人数够了，出队两个玩家
                uint64_t uid1,uid2;
                bool ret = mq.pop(uid1);
                if(ret == false){
                    continue;
                }
                ret = mq.pop(uid2);
                if(ret == false){
                    //由于队列中没有玩家，导致队列出玩家失败
                    //所以另一个玩家要重新回队列进行匹配
                    this->add(uid1);
                    continue;
                }
                //校验两个玩家是否在线，如果有人掉线，则要把另一个人重新添加到匹配队列中
                wsserver_t::connection_ptr conn1 = _om->get_conn_from_hall(uid1);
                if (conn1.get() == nullptr) {
                    this->add(uid2); 
                    continue;
                }
                wsserver_t::connection_ptr conn2 = _om->get_conn_from_hall(uid2);
                if (conn2.get() == nullptr) {
                    this->add(uid1); 
                    continue;
                }
                //为两个玩家创建房间，并将玩家加入房间中
                room_ptr rp = _rm->create_room(uid1,uid2);
                if(rp.get() == nullptr){
                    this->add(uid1);
                    this->add(uid2);
                    continue;
                }
                //对两个玩家进行相应
                Json::Value resp;
                resp["optype"] = "match_success";
                resp["result"] = true;
                std::string body;
                json_util::serialize(resp,body);
                conn1->send(body);
                conn2->send(body);

            }
        }   
        
        //线程入口函数
        void _th_normal_entry(){return handle_match(_q_normal);}
        void _th_high_entry(){return handle_match(_q_high);}
        void _th_super_entry(){return handle_match(_q_super);}
    
    public:
        matcher(room_manager *rm,user_table *ut,online_manager *om)
        :_rm(rm),_ut(ut),_om(om),
        _th_normal(std::thread(&matcher::_th_normal_entry,this)),
        _th_high(std::thread(&matcher::_th_high_entry,this)),
        _th_super(std::thread(&matcher::_th_super_entry,this)){
            DLOG("游戏匹配模块初始化完毕！");
        }

        bool add(uint64_t uid){
            //根据玩家的天梯分数，来判定玩家档次，添加到不同的匹配队列
            //根据用户ID，获取玩家信息
            Json::Value user;
            bool ret = _ut->select_by_id(uid,user);
            if(ret == false){
                DLOG("获取玩家：%ld 信息失败！！",uid);
                return false;
            }
            int score = user["score"].asInt();
            //添加到指定队列
            if (score < 2000) {
                _q_normal.push(uid);
            }else if (score >= 2000 && score < 3000) {
                _q_high.push(uid);
            }else {
                _q_super.push(uid);
            }
            return true;
        }

        bool del(uint64_t uid){
            Json::Value user;
            bool ret = _ut->select_by_id(uid, user);
            if (ret == false) {
                DLOG("获取玩家:%ld 信息失败！！", uid);
                return false;
            }
            int score = user["score"].asInt();
            // 2. 添加到指定的队列中
            if (score < 2000) {
                _q_normal.remove(uid);
            }else if (score >= 2000 && score < 3000) {
                _q_high.remove(uid);
            }else {
                _q_super.remove(uid);
            }
            return true;
        }

};





#endif